Skip to content

fix(integrations): pre-approve Cursor Band tools and surface agent crashes - #750

Merged
AlexanderZ-Band merged 17 commits into
mainfrom
fix/fixcursoracp-pre-approve-bands-mcp-tools-and-surfa-INT-1644
Oct 12, 2026
Merged

AlexanderZ-Band merged 17 commits into
mainfrom
fix/fixcursoracp-pre-approve-bands-mcp-tools-and-surfa-INT-1644

Conversation

@AlexanderZ-Band

@AlexanderZ-Band AlexanderZ-Band commented Oct 6, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Cursor's registered Band MCP tools receive per-call approval during a live turn. Native and other-server tools retain the configured approval policy. Unexpected ACP subprocess exits report their exit code and a bounded stderr tail, including when a background helper keeps stderr open.

Fixes INT-1644.

Changes

  • Share Cursor tool canonicalization between permission handling and stream narration. Validate both halves of display titles, support MCP rawInput names, and retain custom-name collision protection.
  • Require a matching active turn for the Band approval exemption; interrupt and cleanup revoke that session identity. Select only an offered allow-once option for the exemption. Without one, use the configured approval policy.
  • Observe subprocess exit independently of inherited pipe EOF, with a bounded final stderr flush. Preserve crash classification with buffered stdout and connection failures. Diagnostic reader failures remain best-effort during shutdown; cancellation still propagates.
  • Keep runtime startup/shutdown serialized. Retain focused ACP lifecycle modules and existing public exports.
  • Add regression coverage and repair permission/recovery peers to invoke real HTTP MCP replies under the current Band-tool-authoritative reply contract.
  • Simplify the live repair helper to its intended single native permission decision, preserving the rejection of extra room requests.

Latest review fixes on 8de90d0

The three additional nonblocking comments were confirmed and fixed:

  • Preserve original display titles and validated rawInput MCP server identity separately from normalized tool names. A bare native title matching a registered tool no longer receives the Band exemption; strict Band titles and Band rawInput retain it, while foreign rawInput cannot use a misleading Band title.
  • Keep an interrupted or cleaned-up Cursor turn closed. Completing session setup cannot rebind it or start its prompt.
  • Record disconnected ACP session operations consistently, including new/load/config/cancel/close, so cleanup retains unexpected exit-code and stderr diagnostics. Recoverable JSON-RPC request errors retain their existing behavior.

Validation: Ruff check, Ruff format check and Pyrefly passed. Full prescribed unit gate: 7,926 passed, 170 skipped, 81 warnings, 270.94s; integration and live E2E directories excluded. Focused ACP/adapter gate: 600 passed, 10 skipped (existing opt-in real Codex ACP E2E cases). Six new regression rows failed before the fixes and pass afterward; foreign-MCP provenance has an additional regression. Tests exercise real in-process ACP and stdio subprocess boundaries. CI on this new head is pending; historical CI and live acceptance evidence below is unchanged.

Earlier review disposition

All nine new review threads were evaluated, replied to, and resolved. Seven findings were confirmed and fixed; the canonicalization finding was partly right and its small pre-existing external-MCP turn-effect defect was fixed. The proposed lock release was rejected because it permits overlapping teardown/respawn of shared runtime state. The duplicate injected-MCP text-reply claim does not hold on the current reply contract.

Validation on 4de725e

  • Ruff check, Ruff format check, and Pyrefly: passed.
  • Full local unit gate: 7,856 passed, 170 skipped, 81 warnings, 229.62s. Integration and live E2E directories excluded as prescribed by the unit command.
  • Focused ACP/adapter suite: 592 passed, 10 skipped. Final adapter suite including the added stale-cleanup regression: 99 passed.
  • Changed ACP documentation: 1 passed.
  • Pre-fix regression evidence: stderr 4 failed; permission 19 failed, 2 passed; external reply identity 2 failed, 4 passed. Post-fix cases pass, including real stdio/inherited-pipe and HTTP MCP boundaries.
  • All 27 applicable PR checks passed on this head, including Ubuntu and Windows Python 3.11, 3.12, and 3.13. No failed or pending checks. CI run, coverage run. The weekly coverage report is intentionally skipped for PR events; the coverage gate passed.

Live acceptance remains incomplete

The current repair workflow run used repo-root .env.test and disabled retries: 1 failed, 1 deselected in 72.69s. Agent provisioning returned platform HTTP 500 Internal Server Error, before Cursor or the workflow body ran. No pytest retry was performed; the generated REST client retains its default two HTTP retries. This is not a live pass.

Earlier focused real-platform Cursor repair/restart validation on 61c575ed: 2 passed, retries disabled. That evidence predates these review fixes.

The earlier complete backends lane was unsuccessful on its first attempt: 146 passed, 396 skipped, 1 failed, 1 fixture error, 1 recovered retry. A second-user authorized-member case remains blocked by the recorded 403 plan_required entitlement failure. These failures are not erased by local/CI passes or focused rechecks.

Shared Phoenix channel recovery is tracked separately in INT-1700; attribution of the historical Cursor timeout to that defect remains unproven.

Provisioning failure investigation

Independent registration with HTTP retries explicitly disabled reproduced the original 500. Both payloads pass the live OpenAPI schema; isolated toolkit and workflow experiments exclude serialization and model execution as the source. Subsequent calls returned a transport timeout, 403 plan_required, and 201 success, so the dev platform boundary is intermittent rather than the request being permanently invalid. The current sibling platform controller has an empirically confirmed error-handling gap: an upstream {:error, {:badrpc, reason}} raises CaseClauseError instead of reaching the existing HTTP 503 fallback. Its attribution to the observed 500 remains unproven without backend logs.

@linear-code

linear-code Bot commented Oct 6, 2026 •

Copy link
Copy Markdown

INT-1644

INT-1700

@AlexanderZ-Band AlexanderZ-Band changed the title fix(acp): pre-approve Cursor Band tools and surface agent crashes fix(integrations): pre-approve Cursor Band tools and surface agent crashes Oct 6, 2026
@AlexanderZ-Band
AlexanderZ-Band requested a review from a team October 6, 2026 09:34

@AlexanderZ-Band AlexanderZ-Band left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Four verified in-scope findings: startup-cancellation diagnostics, custom-tool coverage, failure-triggered cleanup before stdout EOF, and custom-name ownership collision. Fixing under the requested --fix workflow. GitHub rejected REQUEST_CHANGES because the authenticated user is this PR author; submitting as COMMENT.

Comment thread src/band/integrations/acp/client_runtime.py
Comment thread tests/adapters/test_cursor_acp_adapter.py
Comment thread src/band/integrations/acp/stderr.py
Comment thread src/band/adapters/cursor_acp.py Outdated

@amit-gazal-band amit-gazal-band left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Requesting changes for one blocker: a background helper that inherits stderr hides the agent's crash and makes every stop() take 5s (inline on stderr.py:53, reproduced with a real subprocess).

Everything else inline is an FYI: worth a look, but it doesn't block merge.

The last two commits already fixed two earlier findings: a cancelled start() is no longer logged as a crash, and the e2e test now uses the shared is_cursor_band_tool helper instead of its own copy.

Checks at the reviewed commit: ruff check/format pass. The unit suite (--extra dev, excluding integration/e2e/mcp) had 2 failures, both in test_strands_adapter.py; they also fail on main, so this PR didn't cause them. The live e2e lanes were not run.

Comment thread src/band/integrations/acp/stderr.py Outdated
Comment thread src/band/integrations/acp/client_runtime.py
Comment thread src/band/integrations/acp/stderr.py
Comment thread src/band/integrations/acp/stderr.py
Comment thread src/band/adapters/cursor_acp.py Outdated
Comment thread src/band/adapters/cursor_acp.py Outdated
Comment thread src/band/adapters/cursor_acp.py Outdated
Comment thread src/band/integrations/acp/cursor.py
Comment thread tests/e2e/baseline/smoke/behavior/test_cursor_workflows.py Outdated

@amit-gazal-band amit-gazal-band left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving. I left three non-blocking comments: two are worth a follow-up, and one is a very low-risk edge case.

Comment thread src/band/integrations/acp/cursor.py
Comment thread src/band/adapters/cursor_acp.py
Comment thread src/band/integrations/acp/sessions.py Outdated
@AlexanderZ-Band
AlexanderZ-Band added this pull request to the merge queue Oct 12, 2026
Merged via the queue into main with commit ded7cd9 Oct 12, 2026
28 checks passed
@AlexanderZ-Band
AlexanderZ-Band deleted the fix/fixcursoracp-pre-approve-bands-mcp-tools-and-surfa-INT-1644 branch October 12, 2026 03:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants